<?php

/**
 * This file is part of Webman AI.
 *
 * @author    walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link      http://www.workerman.net/
 */

namespace plugin\ai\app\controller;

use Intervention\Image\ImageManagerStatic;
use plugin\ai\api\Image;
use plugin\ai\app\event\data\ModelRequestData;
use plugin\ai\app\event\data\ModelResponseData;
use plugin\ai\app\service\Common;
use plugin\ai\app\service\User;
use support\exception\BusinessException;
use support\Log;
use support\Request;
use support\Response;
use Throwable;
use Webman\Event\Event;
use Workerman\Http\Client;
use Workerman\Protocols\Http\Chunk;

class ImageController extends Base
{
    /**
     * 不需要登录的方法
     *
     * @var string[]
     */
    protected $noNeedLogin = ['generations', 'get'];

    /**
     * 生成图片
     * @param Request $request
     * @return Response
     * @throws BusinessException
     */
    public function generations(Request $request): Response
    {
        $model = $request->post('model');
        if ($error = static::tryReduceBalance($model)) {
            return json(['error' => ['message' => $error]]);
        }

        $userId = session('user.id') ?? session('user.uid');
        $prompt = $request->post('prompt');
        $number = $request->post('n', 1);
        $size = $request->post('size', '1024x1024');

        $connection = $request->connection;
        $modelRequestData = new ModelRequestData();
        $modelRequestData->data = [
            'model' => $model,
            'prompt' => $prompt,
            'n' => $number,
            'size' => $size
        ];
        $modelRequestData->options = [
            'complete' => function ($json) use ($connection, $model, $modelRequestData, $userId) {
                $responseData = new ModelResponseData();
                $responseData->data = $json;
                $responseData->modelRequestData = $modelRequestData;
                Event::dispatch('ai.image.generations.response', $responseData);
                $result = json_encode($responseData->data);
                if (isset($responseData->data['error'])) {
                    Log::error($result);
                    $modelType = Common::getModelType($model);
                    if ($userId && User::getBalance($userId, $modelType) > 0) {
                        User::addBalance($userId, $modelType);
                    }
                }
                $connection->send(new Chunk($result));
                $connection->send(new Chunk(''));
            }
        ];
        Event::dispatch('ai.image.generations.request', $modelRequestData);
        Image::generations($modelRequestData->data,$modelRequestData->options);

        return response()->withHeaders([
            "Content-Type" => "application/json",
            "Transfer-Encoding" => "chunked",
        ]);
    }

    /**
     * 获取图片(带缓存)
     * @param Request $request
     * @return Response
     * @throws Throwable
     */
    public function get(Request $request): Response
    {
        $url = $request->get('url');
        $md5 = md5($url);
        $type = $request->get('type', '');
        if (!in_array($type, ['', 'sm', 'thumb'])) {
            return response('400 Bad Request', 400);
        }
        $file = runtime_path("tmp/image/$md5.png");
        if (is_file($file)) {
            return \response()->file($file);
        }
        $date = $request->get('date', '');
        if (!$date || !preg_match('/\d{8}/', $date)) {
            return response('400 Bad Request', 400);
        }
        $model = $request->get('model', '');
        if (!in_array($model, ['midjourney', 'dalle'])) {
            return response('404 Image Not Found', 404);
        }
        $type = $type ? "-$type" : '';
        $file = runtime_path("data/image/$model/$date/$md5{$type}.png");
        $bigfile = runtime_path("data/image/$model/$date/$md5.png");
        $smallfile = runtime_path("data/image/$model/$date/$md5-sm.png");
        $thumbFile = runtime_path("data/image/$model/$date/$md5-thumb.png");
        if (is_file($file)) {
            return \response()->file($file);
        }
        // 通过url获取域名
        $http = new Client(['timeout' => 600]);
        $connection = $request->connection;
        $path = parse_url($url, PHP_URL_PATH);
        if ($path === '/app/ai/image/get') {
            return response('400 Image Not Found', 400);
        }

        $http->request($url, [
            'success' => function (\Workerman\Http\Response $response) use ($connection, $bigfile, $smallfile, $thumbFile, $file, $url) {
                if (!is_dir($path = dirname($bigfile))) {
                    mkdir($path, 0777, true);
                }
                file_put_contents($bigfile, $response->getBody());
                if (!getimagesize($bigfile)) {
                    unlink($bigfile);
                    Log::error("ImageController get error not img $url");
                    $connection->send(new Chunk(''));
                    return;
                }
                // 通过文件后缀判断是否是webp格式
                $urlPath = parse_url($url, PHP_URL_PATH);
                $ext = $urlPath ? pathinfo($urlPath, PATHINFO_EXTENSION) : '';
                if ($ext === 'webp' && !isset(gd_info()['WebP Support'])) {
                    $connection->send(new Chunk(file_get_contents($bigfile)));
                    $connection->send(new Chunk(''));
                    return;
                }

                try {
                    $img = ImageManagerStatic::make($bigfile);
                    $img->resize(512, null, function ($constraint) {
                        $constraint->aspectRatio();
                        $constraint->upsize();
                    });
                    $img->save($smallfile);
                    $img->resize(256, null, function ($constraint) {
                        $constraint->aspectRatio();
                        $constraint->upsize();
                    });
                    $img->save($thumbFile);
                } catch (Throwable $e) {
                    if (is_file($bigfile)) {
                        unlink($bigfile);
                    }
                    if (is_file($smallfile)) {
                        unlink($smallfile);
                    }
                    if (is_file($smallfile)) {
                        unlink($thumbFile);
                    }
                    Log::error($e);
                    $connection->send(new Chunk(''));
                    return;
                }
                $connection->maxSendBufferSize = 20 * 1024 * 1024;
                $connection->send(new Chunk(file_get_contents($file)));
                $connection->send(new Chunk(''));
            },
            'error' => function ($e) use ($connection) {
                Log::error($e);
                $connection->send(new Chunk(''));
            }
        ]);
        return response()->withHeaders([
            "Content-Type" => "image/png",
            "Transfer-Encoding" => "chunked",
        ]);
    }
}